package com.newhero.security;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.security.Key;
import java.security.interfaces.RSAPublicKey;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;

/**
*
* 创建时间 2017-7-5
*
* <li>Project: BasePro
* <li>Packgae:com.newhero.security
* <li>File Name: LicenseUtil.java
* <li>Description:
* <li>Copyright: Copyright (c) 2009-2012 Newhero Co. Ltd.
* <li>Company: 河北新禾科技有限公司
*
* @author 王士江
* @version 1.0
* $Id: LicenseUtil.java,v 1.0 2017-7-5 上午11:08:45 wsjjiang Exp $
*/

public class PubLicenseUtil  implements Serializable
{
    private static final long serialVersionUID = 2425971237592505266L;
    private static PubLicenseUtil rsa = new PubLicenseUtil();
    @SuppressWarnings("unused")
	private String license = "";
    
    /** 
     * 加密/解密算法/工作模式/填充方式 
     * */  
    private static final String CIPHER_ALGORITHM="RSA/ECB/PKCS5Padding";  
    
    private PubLicenseUtil()
    {
        
    }
    
    public static PubLicenseUtil getInstance()
    {
        return rsa;
    }

    /**
     * 加密 　　
     * 
     * @param key
     *            加密的密钥 　　
     * @param data
     *            待加密的明文数据 　　
     * @return 加密后的数据
     */
    public static byte[] encrypt(Key key, byte[] data) 
    {
        try 
        {
            Cipher cipher = Cipher.getInstance("RSA",new org.bouncycastle.jce.provider.BouncyCastleProvider());
            cipher.init(Cipher.ENCRYPT_MODE, key);
            // 获得加密块大小，如：加密前数据为128个byte，而key_size=1024
            // 加密块大小为127
            // byte,加密后为128个byte;因此共有2个加密块，第一个127
            // byte第二个为1个byte
            int blockSize = cipher.getBlockSize();
            // 获得加密块加密后块大小
            int outputSize = cipher.getOutputSize(data.length);
            int leavedSize = data.length % blockSize;
            int blocksSize = leavedSize != 0 ? data.length / blockSize + 1 : data.length / blockSize;
            byte[] raw = new byte[outputSize * blocksSize];
            int i = 0;
            while (data.length - i * blockSize > 0) 
            {
                //这里面doUpdate方法不可用，查看源代码后发现每次doUpdate后并没有什么实际动作除了把byte[]放到ByteArrayOutputStream中，
                //而最后doFinal的时候才将所有的byte[]进行加密，可是到了此时加密块大小很可能已经超出了OutputSize所以只好用dofinal方法。
                if (data.length - i * blockSize > blockSize)
                {
                    cipher.doFinal(data, i * blockSize, blockSize, raw, i* outputSize);
                }
                else
                {
                    cipher.doFinal(data, i * blockSize, data.length - i * blockSize, raw, i * outputSize);
                }
                i++;
            }
            return raw;
        } 
        catch (Exception e) 
        {
            e.printStackTrace();
            return null;
        }
    }
    
    /**
     * 加密 　　
     * 
     * @param key
     *            加密的密钥 　　
     * @param data
     *            待加密的明文数据 　　
     * @return 加密后的数据
     */
    public static String encrypt(Key key, String strData) 
    {
        byte[] data = strData.getBytes();
        byte[] rtnData = encrypt(key,data);
        return rtnData.toString();
    }

    /**
     * 解密 　　
     * 
     * @param key
     *            解密的密钥 　　
     * @param raw
     *            已经加密的数据 　　
     * @return 解密后的明文 　
     */
    @SuppressWarnings("static-access")
	public static byte[] decrypt(Key key, byte[] raw) 
    {
        try 
        {
            Cipher cipher = Cipher.getInstance("RSA",new org.bouncycastle.jce.provider.BouncyCastleProvider());
            cipher.init(cipher.DECRYPT_MODE, key);
            int blockSize = cipher.getBlockSize();
            ByteArrayOutputStream bout = new ByteArrayOutputStream(64);
            int j = 0;
            while (raw.length - j * blockSize > 0) 
            {
                bout.write(cipher.doFinal(raw, j * blockSize, blockSize));
                j++;
            }
            return bout.toByteArray();
        } 
        catch (Exception e) 
        {
            return null;
        }
    }
    
    @SuppressWarnings("static-access")
	public static byte[] decrypt(byte[] raw) 
    {
        try 
        {
            Cipher cipher = Cipher.getInstance("RSA",new org.bouncycastle.jce.provider.BouncyCastleProvider());
            cipher.init(cipher.DECRYPT_MODE, PubLicenseUtil.getInstance().getKey());
            int blockSize = cipher.getBlockSize();
            ByteArrayOutputStream bout = new ByteArrayOutputStream(64);
            int j = 0;
            while (raw.length - j * blockSize > 0) 
            {
                bout.write(cipher.doFinal(raw, j * blockSize, blockSize));
                j++;
            }
            return bout.toByteArray();
        } 
        catch (Exception e) 
        {
            return null;
        }
    }
    
    /**
     * 解密 　　
     * 
     * @param key
     *            解密的密钥 　　
     * @param raw
     *            已经加密的数据 　　
     * @return 解密后的明文 　
     */
    public static String decrypt(Key key, String strData) 
    {
        byte[] raw = strData.getBytes();
        byte[] rtnData = decrypt(key,raw);
        return rtnData.toString();
    }
    
    /**
     * 根据加密KEY文件存放路径返回实例
     * @param filePath  路径
     * @return          成功返回实例/失败返回null
     */
    private RSAPublicKey getKey()
    {       
        return getKey("NewHeroKey.dat");
    }
    
    private RSAPublicKey getKey(String strKey)
    {       
        RSAPublicKey pubKey = null;
        try {
            InputStream is = PubLicenseUtil.class.getResourceAsStream(strKey);
            if(is == null)
            {
                System.out.println("通过公钥文件获取密钥KEY失败！");
                return null;
            }
            ObjectInputStream oos = new ObjectInputStream(is);
            pubKey = (RSAPublicKey) oos.readObject();
            oos.close();
        } catch (Exception e) 
        {
            e.printStackTrace();
            System.out.println("通过公钥文件获取密钥KEY失败！");
            return null;
        }
        return pubKey;
    }
    
    /**
      * 文件file进行加密并保存目标文件destFile中

      * @param srcFileName
      *            要加密的文件 如c:/test/srcFile.txt
      * @param destFileName
      *            加密后存放的文件名 如c:/加密后文件.txt
      */

    public void encryptFile(String srcFileName,String destFileName,Key key) throws Exception 
    {
        OutputStream outputWriter = null;
        InputStream inputReader = null;
        CipherInputStream cis = null;
        try {
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM,new org.bouncycastle.jce.provider.BouncyCastleProvider());
            byte[] buf = new byte[1024];
            int bufl;
            cipher.init(Cipher.ENCRYPT_MODE, key);
            outputWriter = new FileOutputStream(destFileName);
            inputReader = new FileInputStream(srcFileName);
            cis = new CipherInputStream(inputReader, cipher);
            while ((bufl = cis.read(buf)) > 0) {
                outputWriter.write(buf, 0, bufl);
            }
            outputWriter.flush();
            outputWriter.close();
            cis.close();
            inputReader.close();
        } 
        catch (Exception e) 
        {
            throw e;
        } 
        finally 
        {
            try 
            {
                if (outputWriter != null)
                {
                    outputWriter.close();
                }
                if (cis != null)
                {
                    cis.close();
                }
                if (inputReader != null)
                {
                    inputReader.close();
                }
            } 
            catch (Exception e) 
            {
            }
        }
    }

    /**
      * 文件file进行加密并保存目标文件destFile中

      * @param srcFileName
      *            已加密的文件 如c:/加密后文件.txt
      * @param destFileName
      *            解密后存放的文件名 如c:/ test/解密后文件.txt
      */

    public void decryptFile(String srcFileName,String destFileName,Key key) throws Exception 
    {
        OutputStream outputWriter = null;
        InputStream inputReader = null;
        CipherOutputStream cos = null;
        try {
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM,new org.bouncycastle.jce.provider.BouncyCastleProvider());
            byte[] buf = new byte[1024];
            int bufl;
            cipher.init(Cipher.DECRYPT_MODE, key);
            outputWriter = new FileOutputStream(destFileName);
            inputReader = new FileInputStream(srcFileName);
            cos = new CipherOutputStream(outputWriter, cipher);
            while ((bufl = inputReader.read(buf)) >= 0) 
            {
                cos.write(buf, 0, bufl);
            }
            cos.flush();
            outputWriter.flush();
            outputWriter.close();
            cos.close();
            inputReader.close();
        } 
        catch (Exception e) 
        {
            throw e;
        } 
        finally 
        {
            try {
                if (outputWriter != null) 
                {
                    outputWriter.close();
                }
                if (cos != null) 
                {
                    cos.close();
                }
                if (inputReader != null) {
                    inputReader.close();
                }
            } 
            catch (Exception e) 
            {
            }
        }
    }
    
    @SuppressWarnings("static-access")
	public static boolean checkLicense()
    {
        String license = new String(PubLicenseUtil.getInstance().decrypt(NewheroLicense.getInstance().getThisLicense()));
        if(NewheroLicense.getInstance().macAddrIsNotNull() && license.indexOf(NewheroLicense.getInstance().getThisMacAddr())!=-1)
        {
            return true;
        }
        return false;
    }
    
    public static byte[] getLicense()
    {
        InputStream in = PubLicenseUtil.class.getResourceAsStream("license.newhero");
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        byte[] tmpbuf = new byte[1024];
        int count = 0;
        try
        {
            while ((count = in.read(tmpbuf)) != -1) 
            {
                bout.write(tmpbuf, 0, count);
                tmpbuf = new byte[1024];
            }
        } 
        catch (IOException e)
        {
            
        }
        finally
        {
            try
            {
                in.close();
            } 
            catch (IOException e)
            {
            }
        }
        return bout.toByteArray();
    }
    
    @SuppressWarnings("static-access")
	public static void main(String[] args) throws IOException
    {
        FileInputStream in = new FileInputStream(new File("D:/CreateNewheroLicense/license.seening"));
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        byte[] tmpbuf = new byte[1024];
        int count = 0;
        while ((count = in.read(tmpbuf)) != -1) {
            bout.write(tmpbuf, 0, count);
            tmpbuf = new byte[1024];
        }
        in.close();
        byte[] orgData = bout.toByteArray();
        byte[] priKey = PubLicenseUtil.getInstance().decrypt(orgData);
        System.out.println(new String(priKey));
        /*
        NewheroLicense.getInstance().initLicense();
        System.out.println(LicenseUtil.checkLicense());
        System.out.println(NewheroLicense.getInstance().getThisMacAddr());
        System.out.println(new String(NewheroLicense.getInstance().getThisLicense()));
        */
    }
}